package com.elf.zero.utils;

import android.content.Context;
import android.security.KeyPairGeneratorSpec;
import android.util.Base64;

import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.MGF1ParameterSpec;
import java.util.Calendar;

import javax.crypto.Cipher;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import javax.security.auth.x500.X500Principal;

public class KeyStoryManager {

    private final static Charset Charset_UTF_8 = Charset.forName("UTF-8");
    private final static String AndroidKeyStore = "AndroidKeyStore";
    private final static String Algorithm = "RSA";

    private static final String ECB_PADDING = "RSA/ECB/OAEPwithSHA-256andMGF1Padding";//加密填充方式

    private final static int DECRYPT_BLOCK = 256;
    private final static int ENCRYPT_BLOCK = 190;

    private Context context;
    private KeyStore keyStore;
    private X500Principal x500Principal;

    public KeyStoryManager(Context context) {
        this.context = context;
    }

    public void init() throws Exception {
        if (keyStore == null) {
            keyStore = KeyStore.getInstance("AndroidKeyStore");
            keyStore.load(null);
            x500Principal = new X500Principal("CN=Duke, OU=JavaSoft, O=Sun Microsystems, C=US");
        }
    }

    private boolean containsAlias(String alias) throws Exception {
        return keyStore.containsAlias(alias);
    }

    public String encrypt(String alias, String data) throws Exception {
        //取出密钥
        KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias, null);
        RSAPublicKey publicKey = (RSAPublicKey) privateKeyEntry.getCertificate().getPublicKey();

        OAEPParameterSpec oaepParameterSpec
                = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT);

        //加密
        byte[] sourceBytes = data.getBytes(Charset_UTF_8);
        int blockCount = (sourceBytes.length / ENCRYPT_BLOCK);
        if ((sourceBytes.length % ENCRYPT_BLOCK) != 0) {
            blockCount += 1;
        }

        ByteArrayOutputStream bos = new ByteArrayOutputStream(blockCount * ENCRYPT_BLOCK);
        Cipher cipher = Cipher.getInstance(ECB_PADDING);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey, oaepParameterSpec);

        for (int offset = 0; offset < sourceBytes.length; offset += ENCRYPT_BLOCK) {
            int inputLen = (sourceBytes.length - offset);
            if (inputLen > ENCRYPT_BLOCK) {
                inputLen = ENCRYPT_BLOCK;
            }
            byte[] encryptedBlock = cipher.doFinal(sourceBytes, offset, inputLen);
            bos.write(encryptedBlock);
        }

        bos.close();
        byte[] byData = bos.toByteArray();
        return new String(Base64.encode(byData, Base64.NO_WRAP), Charset_UTF_8);
    }

    public String decrypt(String alias, String data) throws Exception {
        //取出密钥
        KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);
        PrivateKey privateKey = privateKeyEntry.getPrivateKey();

        //解密
        byte[] encrypt_Data = Base64.decode(data, Base64.NO_WRAP);

        int blockCount = (encrypt_Data.length / DECRYPT_BLOCK);
        if ((encrypt_Data.length % DECRYPT_BLOCK) != 0) {
            blockCount += 1;
        }
        ByteArrayOutputStream bos = new ByteArrayOutputStream(blockCount * DECRYPT_BLOCK);

        OAEPParameterSpec oaepParameterSpec
                = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT);

        Cipher cipher = Cipher.getInstance(ECB_PADDING);
        cipher.init(Cipher.DECRYPT_MODE, privateKey, oaepParameterSpec);
        for (int offset = 0; offset < encrypt_Data.length; offset += DECRYPT_BLOCK) {
            int inputLen = (encrypt_Data.length - offset);

            if (inputLen > DECRYPT_BLOCK) {
                inputLen = DECRYPT_BLOCK;
            }

            byte[] decryptedBlock = cipher.doFinal(encrypt_Data, offset, inputLen);
            bos.write(decryptedBlock);
        }

        bos.close();
        byte[] sourceBytes = bos.toByteArray();
        return new String(sourceBytes, Charset_UTF_8);
    }

    public void getKeystore(String alias) throws Exception {

        if (containsAlias(alias)) {
            return;
        }

        Calendar endDate = Calendar.getInstance();
        endDate.add(Calendar.YEAR, 30);

        KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(context.getApplicationContext())
                .setAlias(alias)
                .setSubject(x500Principal)
                .setSerialNumber(BigInteger.ONE)
                .setStartDate(Calendar.getInstance().getTime())
                .setEndDate(endDate.getTime())
                .build();
        KeyPairGenerator generator = KeyPairGenerator.getInstance(Algorithm, AndroidKeyStore);
        generator.initialize(spec);

        generator.generateKeyPair();
    }
}
